package ch.zbw.timeline.service;

import java.sql.Time;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

import ch.zbw.timeline.domain.Allocation;
import ch.zbw.timeline.domain.DailyPresence;
import ch.zbw.timeline.domain.Employee;
import ch.zbw.timeline.domain.Entry;
import ch.zbw.timeline.domain.Period;
import ch.zbw.timeline.domain.Statistic;
import ch.zbw.timeline.domain.YearConfig;
import ch.zbw.timeline.repository.AllocationRepository;
import ch.zbw.timeline.repository.EntryRepository;
import ch.zbw.timeline.repository.YearConfigRepository;
import ch.zbw.timeline.repository.hibernate.HibernateAllocationRepository;
import ch.zbw.timeline.repository.hibernate.HibernateEntryRepository;
import ch.zbw.timeline.repository.hibernate.HibernateYearConfigRepository;

/**
 * Class that handle the statistic. They can read the database to make some
 * individual statistic by period for all users
 * 
 * @author Andreas Kaderli
 * @version 25.12.2011
 * 
 */
public class ZbwStatisticService implements StatisticService {

	private ZbwDailyPresenceService dailyPresenceService = new ZbwDailyPresenceService();
	private ZbwEntryService entryService = new ZbwEntryService();
	private YearConfigRepository yearConfigRepository = new HibernateYearConfigRepository();
	private AllocationRepository allocationRepository = new HibernateAllocationRepository();
	private EntryRepository entryRepository = new HibernateEntryRepository();

	private int beginDay = 0;
	private int beginMonth = 0;
	private int endDay = 0;
	private int endMonth = 0;
	private int year = 0;
	private Employee employee;
	private Calendar beginOfPeriod;
	private Calendar endOfPeriod;
	private YearConfig yearConfig;
	private final double MILLISECONDS_IN_DAY = 1000 * 60 * 60 * 24;
	private DecimalFormat df = new DecimalFormat("#0.0");

	// private YearConfigRepository yearConfigRepository = new
	// MockYearConfigRepository();

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ch.zbw.timeline.service.StatisticService#generateStatistic(ch.zbw.timeline
	 * .domain.Employee, ch.zbw.timeline.domain.Period)
	 */
	@Override
	public Statistic generateStatistic(Employee employee, Period period) {
		if (employee != null && period != null) {
			Statistic statistic = new Statistic();
			TimeZone.setDefault(TimeZone.getTimeZone("GMT"));

			this.beginOfPeriod = period.getBeginOfPeriod();
			this.endOfPeriod = period.getEndOfPeriod();

			// The day, month and year from the period
			this.beginDay = beginOfPeriod.get(Calendar.DATE);
			this.beginMonth = beginOfPeriod.get(Calendar.MONTH) + 1;
			this.endDay = endOfPeriod.get(Calendar.DATE);
			this.endMonth = endOfPeriod.get(Calendar.MONTH) + 1;
			this.year = beginOfPeriod.get(Calendar.YEAR);

			this.employee = employee;
			this.yearConfig = yearConfigRepository.getYearConfigByYear(year);

			// create the statistic
			statistic.setLastName(getLastName());
			statistic.setFirstName(getFirstName());
			statistic.setUserName(getUserName());
			statistic.setBeginOfPeriod(beginOfPeriod);
			statistic.setEndOfPeriod(endOfPeriod);
			statistic.setDailyPresenceByPeriod(getTotalTimeToWorkInPeriod());
			statistic.setSoll(getTotalPresenceTimeFromPeriod());
			statistic.setIst(getWorkedHoursFromPeriod());
			statistic.setOverTimeInPeriod(overtimeInPeriod());
			statistic.setPercentOfOverTime(String.valueOf(yearConfig
					.getFlexitimePercent()));
			statistic.setUsedHolidayInPeriod(calculateUsedHolidayInPeriod());
			statistic.setUnusedHolidayInYear(calculateUnusedHolidayInPeriod());
			statistic.setAllEntriesFromPeriod(getEntryInPeriod());
			statistic.setEntryChronologic(getDateOfDailyPresence());
			statistic.setDailyWorkTime(getTimeOfDate());
			statistic.setWeeklyWorkTime(getTimeOfWeek());

			return statistic;
		}

		return null;

	}

	/**
	 * Return the employee last name
	 * 
	 * @return last name
	 */
	private String getLastName() {
		return employee.getLastName();
	}

	/**
	 * Returns the employee first name
	 * 
	 * @return first name
	 */
	private String getFirstName() {
		return employee.getFirstName();
	}

	private String getUserName() {
		return employee.getUserName();
	}

	/**
	 * Calculate the is to be hours from period
	 * 
	 * These methods calculate the is to be hours from period. First they
	 * calculate the working hours from day and then they calculate the duration
	 * of the periods in millisecond. So they multiple the working hour form day
	 * with the millisecond in period an we got the is to worked hours form
	 * period.
	 * 
	 * @return periodWorkingHours is to be hours from period
	 */
	private double getTotalPresenceTimeFromPeriod() {

		double weeklyWorkingHours = yearConfig.getWeeklyWorkingHours();
		double dayWorkingHours = weeklyWorkingHours / 7;

		double diff = Math.abs(endOfPeriod.getTimeInMillis()
				- beginOfPeriod.getTimeInMillis());
		double days = diff / MILLISECONDS_IN_DAY;
		double periodWorkingHours = dayWorkingHours * days;

		return periodWorkingHours;
	}

	/**
	 * These methods calculate the total presence time that a employee worked in
	 * the period
	 * 
	 * @return totalPresenceTime the total presence time
	 */
	private double getTotalTimeToWorkInPeriod() {
		List<DailyPresence> tempDailyPresence = getDailyPresenceInPeriod();
		long totalPresenceTime = 0;
		for (int i = 0; i < tempDailyPresence.size(); i++) {
			DailyPresence dailyPresence = tempDailyPresence.get(i);
			Time presenceTime = dailyPresenceService
					.getPresenceTimeTotal(dailyPresence);
			if (presenceTime != null) {
				totalPresenceTime += presenceTime.getTime();
			}
		}
		return totalPresenceTime / 3600000;
	}

	/**
	 * These methods calculate the overtime in period
	 * 
	 * @return overtime in period
	 */
	private double overtimeInPeriod() {
		double dayWorkingHoursIs = 0.00;
		List<DailyPresence> tempDailyPresence = getDailyPresenceInPeriod();
		double dayWorkingHoursSoll = getSollHours() * tempDailyPresence.size();

		Iterator<DailyPresence> it = tempDailyPresence.iterator();
		while (it.hasNext()) {
			DailyPresence tempDaily = it.next();
			List<Entry> tempEntry = entryService.getAllEntries(tempDaily);

			for (int i = 0; i < tempEntry.size(); i++) {
				dayWorkingHoursIs += tempEntry.get(i).getDuration();

			}

			int morningVacation = tempDaily.getMorningVacation();
			int afternoonVacation = tempDaily.getAfternoonVacation();

			double temp = getSollHours() / 2;
			if (morningVacation == 1 && afternoonVacation == 1) {
				dayWorkingHoursSoll -= temp;
				dayWorkingHoursSoll -= temp;
			} else if (morningVacation == 1) {
				dayWorkingHoursSoll -= temp;
			} else if (afternoonVacation == 1) {
				dayWorkingHoursSoll -= temp;
			}
		}

		return dayWorkingHoursIs - dayWorkingHoursSoll;
	}

	/**
	 * These methods calculate the working hours from period
	 * 
	 * @return workingHours from period
	 */
	private double getWorkedHoursFromPeriod() {
		double workingHours = 0.00;
		List<DailyPresence> tempDailyPresence = getDailyPresenceInPeriod();
		for (int i = 0; i < tempDailyPresence.size(); i++) {
			int idDaily = tempDailyPresence.get(i).getId();
			List<Entry> entry = entryRepository.getAllEntries(idDaily);
			for (int j = 0; j < entry.size(); j++) {
				workingHours += entry.get(j).getDuration();
			}
		}
		return workingHours;
	}

	/**
	 * These methods calculate the used holiday in period
	 * 
	 * @return used holiday in period
	 */
	private double calculateUsedHolidayInPeriod() {
		List<DailyPresence> tempDailyPresence = getDailyPresenceInPeriod();
		double usedHolidayInPeriod = 0;
		double morningVacation = 0;
		double afternoonVacation = 0;
		for (int i = 0; i < tempDailyPresence.size(); i++) {
			if (tempDailyPresence.get(i).getMorningVacation() == 1) {
				morningVacation += 0.5;
			}
			if (tempDailyPresence.get(i).getAfternoonVacation() == 1) {
				afternoonVacation += 0.5;
			}
		}
		return usedHolidayInPeriod = morningVacation + afternoonVacation;
	}

	/**
	 * These methods calculate the unused holiday in period
	 * 
	 * @return unused holiday in period
	 */
	private double calculateUnusedHolidayInPeriod() {

		ZbwEmployeeService service = new ZbwEmployeeService();
		double days = service.getNumbersOfVacationDays(this.employee,
				this.yearConfig);
		double usedHolidayInPeriod = calculateUsedHolidayInPeriod();

		double unusedHolidayInPeriod = days - usedHolidayInPeriod;

		return unusedHolidayInPeriod;

	}

	/**
	 * These methods create a list of chronologic entries
	 * 
	 * @return list of entry
	 */
	private List<HashMap<String, String>> getDateOfDailyPresence() {

		// all daily presence form list
		List<DailyPresence> tempDailyPresence = getDailyPresenceInPeriod();

		HashMap<String, String> hash = null;
		List<HashMap<String, String>> tempList = new ArrayList<HashMap<String, String>>();

		Iterator<DailyPresence> it = tempDailyPresence.iterator();
		while (it.hasNext()) {
			hash = new HashMap<String, String>();
			DailyPresence tempDaily = it.next();
			List<Entry> tempEntry = entryService.getAllEntries(tempDaily);
			String tempDate = createDate(tempDaily);

			// the task form daily presence
			createEntryString(hash, tempEntry, tempDate);

			tempList.add(hash);
		}

		return tempList;

	}

	/**
	 * These method create the date for the statistic
	 * 
	 * @param tempDaily
	 *            the daily presence from period
	 * @return the date of the current daily presence
	 */
	private String createDate(DailyPresence tempDaily) {
		int day = tempDaily.getDay();
		int month = tempDaily.getMonth();
		String tempDate = day + ". " + month + ". " + this.year;
		return tempDate;
	}

	/**
	 * These method create the entry for the statistic
	 * 
	 * @param hash
	 *            the list of entry
	 * @param tempEntry
	 *            the current entry from one daily presence
	 * @param tempDate
	 *            the current day from one daily presence
	 */
	private void createEntryString(HashMap<String, String> hash,
			List<Entry> tempEntry, String tempDate) {
		for (int i = 0; i < tempEntry.size(); i++) {
			int allocationFk = tempEntry.get(i).getAllocationFk();
			Allocation allocation = allocationRepository
					.getAllocationById(allocationFk);

			String process = allocation.getProcess().getDescription();
			String task = allocation.getTask().getDescription();

			String entryString = "<td>" + tempDate + "</td><td>" + task
					+ "</td><td>" + process + "</td><td>"
					+ tempEntry.get(i).getDuration() + "</td>";

			hash.put("Entry" + (i + 1), entryString);
		}
	}

	/**
	 * These method returns all presence time from period
	 * 
	 * @return tempList with total duration from period
	 */
	private List<String> getTimeOfDate() {

		// All daily presence from these period
		List<DailyPresence> tempDailyPresence = getDailyPresenceInPeriod();

		HashMap<String, String> hash = null;
		List<String> tempList = new ArrayList<String>();

		Iterator<DailyPresence> it = tempDailyPresence.iterator();
		while (it.hasNext()) {
			hash = new HashMap<String, String>();
			DailyPresence tempDaily = it.next();
			List<Entry> tempEntry = entryService.getAllEntries(tempDaily);

			// create the date
			String tempDate = createDate(tempDaily);

			// calculate the soll hours
			double dayWorkingHours = getSollHours();

			// calculate the is hours
			double isHoursFromDay = 0;
			for (int i = 0; i < tempEntry.size(); i++) {
				isHoursFromDay += tempEntry.get(i).getDuration();
			}

			int morningVacation = tempDaily.getMorningVacation();
			int afternoonVacation = tempDaily.getAfternoonVacation();

			double temp = dayWorkingHours / 2;
			if (morningVacation == 1 && afternoonVacation == 1) {
				dayWorkingHours -= temp;
				dayWorkingHours -= temp;
			} else if (morningVacation == 1) {
				dayWorkingHours -= temp;
			} else if (afternoonVacation == 1) {
				dayWorkingHours -= temp;
			}

			double overTime = isHoursFromDay - dayWorkingHours;

			String dayString = "<td>" + tempDate + "</td><td>"
					+ dayWorkingHours + "</td><td>" + df.format(isHoursFromDay)
					+ "</td><td>" + df.format(overTime) + "</td>";

			tempList.add(dayString);

		}

		return tempList;

	}

	/**
	 * These methods returns the time of week
	 * 
	 * @return the time of one week
	 */
	private List<String> getTimeOfWeek() {
		List<DailyPresence> tempDailyPresence = getDailyPresenceInPeriod();

		HashMap<String, String> hash = null;
		List<String> tempList = new ArrayList<String>();

		// double weeklyWorkingHours = yearConfig.getWeeklyWorkingHours();

		Iterator<DailyPresence> it = tempDailyPresence.iterator();
		while (it.hasNext()) {
			hash = new HashMap<String, String>();
			DailyPresence tempDaily = it.next();
			List<Entry> tempEntry = entryService.getAllEntries(tempDaily);

			// calculate the is hours
			double isHoursFromDay = 0;
			for (int i = 0; i < tempEntry.size(); i++) {
				isHoursFromDay += tempEntry.get(i).getDuration();
			}

			Calendar cal = Calendar.getInstance();
			cal.setFirstDayOfWeek(Calendar.MONDAY);
			cal.setTime(beginOfPeriod.getTime());
		}

		return tempList;
	}

	/**
	 * These methods create the soll hours for one day
	 * 
	 * @return the soll hours from one day
	 */
	private double getSollHours() {

		double weeklyWorkingHours = yearConfig.getWeeklyWorkingHours();
		double dayWorkingHours = (weeklyWorkingHours / 5);

		return dayWorkingHours;
	}

	/**
	 * These methods return the actually daily presence from period
	 * 
	 * @return daily presence from period
	 */
	private List<DailyPresence> getDailyPresenceInPeriod() {
		List<DailyPresence> dailyPresenceList = dailyPresenceService
				.getAllDailyPresences(employee);
		List<DailyPresence> periodDailyPresenceList = new ArrayList<DailyPresence>();
		for (int i = 0; i < dailyPresenceList.size(); i++) {
			if (dailyPresenceList.get(i).getYear() == year
					&& dailyPresenceList.get(i).getMonth() >= beginMonth
					&& dailyPresenceList.get(i).getMonth() <= endMonth
					&& dailyPresenceList.get(i).getDay() < endDay) {

				periodDailyPresenceList.add(dailyPresenceList.get(i));
			}
		}
		return periodDailyPresenceList;
	}

	/**
	 * These method return all entry from period
	 * 
	 * @return entryList return all entry from period
	 */
	private List<Entry> getEntryInPeriod() {
		List<Entry> entryList = new ArrayList<Entry>();
		DailyPresence dailyPresence = new DailyPresence();
		List<DailyPresence> tempDailyPresence = getDailyPresenceInPeriod();
		for (int i = 0; i < tempDailyPresence.size(); i++) {
			dailyPresence = tempDailyPresence.get(i);
			entryList.addAll(entryService.getAllEntries(dailyPresence));
		}

		return entryList;
	}
}
